package com.dl.dlcommon.aop;


import com.dl.dlcommon.annotation.MethodInfo;
import com.dl.dlcommon.utils.TimeLogger;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 打印方法日志
 *
 * @author DL
 * @create 2024-03-20 16:20
 */
@Aspect
@Component
@Slf4j
@Order(2)
public class MethodInfoAspect {

    /**
     * @within 注解表示在类级别上，@annotation 注解表示在方法级别上。
     *
     * @author DL
     * @date 2024/03/20
     */
    @Pointcut("@within(com.dl.dlcommon.annotation.MethodInfo) || @annotation(com.dl.dlcommon.annotation.MethodInfo)")
    private void pointCut() {
    }

    @Around("pointCut()")
    public Object getMethodInfo(ProceedingJoinPoint point) throws Throwable {
        Signature signature = point.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);

        if (methodInfo == null) {
            // 类级别上的注解
            Class<?> declaringType = method.getDeclaringClass();
            methodInfo = declaringType.getAnnotation(MethodInfo.class);
        }

        // 打印http参数
        logRequestInfo(point);

        // 获取方法参数名
        String[] parameterNames = methodSignature.getParameterNames();
        // 获取方法参数值
        Object[] args = point.getArgs();
        // 使用String.join()方法连接参数对，每个参数对之间用","分隔
        String paramString = IntStream.range(0, parameterNames.length)
                .mapToObj(i -> parameterNames[i] + "=" + args[i])
                .collect(Collectors.joining(","));
        log.info("请求入参:{}", paramString);


        boolean time = methodInfo.time();
        // 如果需要记录方法执行时间，则使用TimeLogger工具类
        if (time){
            TimeLogger.ResultWithTime<Object> resultWithTime = TimeLogger.measureTimeAndResult(() -> {
                try {
                    return point.proceed();
                } catch (Throwable e) {
                    throw new RuntimeException(e);
                }
            });
            return resultWithTime.getResult();
        }else {
            return point.proceed();
        }
    }

    /**
     * 打印http参数
     *
     * @param point 切点
     * @author DL
     * @date 2024/03/20
     */
    private void logRequestInfo(ProceedingJoinPoint point) {
        Signature signature = point.getSignature();

        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (servletRequestAttributes != null) {
            HttpServletRequest httpServletRequest = servletRequestAttributes.getRequest();
            log.info(">>>>>> request start >>>>>>");
            log.info("请求链接：{}", httpServletRequest.getRequestURL().toString());
            log.info("请求类型:{}", httpServletRequest.getMethod());
            log.info("请求方法:{} - {}", signature.getDeclaringTypeName(), signature.getName());
            log.info("请求ip:{}", httpServletRequest.getRemoteAddr());
        }
    }
}
